package downloader

import (
	"fmt"
	"io"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// HTTPDownloader implements the Downloader interface for HTTP/HTTPS
type HTTPDownloader struct {
	BaseDownloader
	client    *http.Client
	url       string
	parts     [][]int64
	partFiles []string
}

// NewHTTPDownloader creates a new HTTP downloader
func NewHTTPDownloader(config *Config) *HTTPDownloader {
	return &HTTPDownloader{
		BaseDownloader: BaseDownloader{
			config:   config,
			stopChan: make(chan struct{}),
		},
		client: &http.Client{},
	}
}

// Download starts the HTTP download
func (d *HTTPDownloader) Download(urlStr string) error {
	d.url = urlStr

	// Get file size and check if server supports range requests
	resp, err := d.client.Head(urlStr)
	if err != nil {
		return fmt.Errorf("failed to get file info: %w", err)
	}
	defer resp.Body.Close()

	size := resp.ContentLength
	supportsRange := resp.Header.Get("Accept-Ranges") == "bytes"

	// Get filename from URL or Content-Disposition
	filename := d.getFilename(resp)
	d.initProgress(size, filename)

	if !supportsRange || d.config.MaxThreads <= 1 {
		return d.singleThreadDownload(filename)
	}
	return d.multiThreadDownload(filename, size)
}

func (d *HTTPDownloader) singleThreadDownload(filename string) error {
	resp, err := d.client.Get(d.url)
	if err != nil {
		return err
	}

	defer resp.Body.Close()
	if resp.StatusCode == http.StatusForbidden {
		// Parse the URL to get the host for referer
		parsedURL, err := url.Parse(d.url)
		if err != nil {
			return fmt.Errorf("failed to parse URL: %w", err)
		}

		// Create a new request with additional headers
		req, err := http.NewRequest("GET", d.url, nil)
		if err != nil {
			return fmt.Errorf("failed to create request: %w", err)
		}

		// Add common browser-like headers
		req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
		req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
		req.Header.Set("Accept-Language", "en-US,en;q=0.5")
		req.Header.Set("Referer", fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host))

		// Make the new request
		resp, err = d.client.Do(req)
		if err != nil {
			return fmt.Errorf("failed retry request: %w", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("failed to download after adding headers: status code %d", resp.StatusCode)
		}
	}
	out, err := d.createOutputFile(filename)
	if err != nil {
		return err
	}
	defer out.Close()

	return d.copyWithProgress(out, resp.Body)
}

func (d *HTTPDownloader) multiThreadDownload(filename string, size int64) error {
	// Calculate part sizes
	partSize := size / int64(d.config.MaxThreads)
	d.parts = make([][]int64, d.config.MaxThreads)
	d.partFiles = make([]string, d.config.MaxThreads)

	for i := 0; i < d.config.MaxThreads; i++ {
		start := int64(i) * partSize
		end := start + partSize - 1
		if i == d.config.MaxThreads-1 {
			end = size - 1
		}
		d.parts[i] = []int64{start, end}
		d.partFiles[i] = fmt.Sprintf("%s.part%d", filename, i)
	}

	// Download parts
	var wg sync.WaitGroup
	errs := make(chan error, d.config.MaxThreads)

	for i := 0; i < d.config.MaxThreads; i++ {
		wg.Add(1)
		go func(partNum int) {
			defer wg.Done()
			if err := d.downloadPart(partNum); err != nil {
				errs <- err
			}
		}(i)
	}

	// Wait for all downloads to complete
	wg.Wait()
	close(errs)

	// Check for errors
	for err := range errs {
		if err != nil {
			d.cleanupParts()
			return err
		}
	}
	// Merge parts
	return d.mergeParts(filename)
}

func (d *HTTPDownloader) downloadPart(partNum int) error {
	req, err := http.NewRequest("GET", d.url, nil)
	if err != nil {
		return err
	}

	start, end := d.parts[partNum][0], d.parts[partNum][1]
	req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))

	resp, err := d.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	filenamePart := filepath.Join(d.config.TempDir, d.partFiles[partNum])
	out, err := os.Create(filenamePart)
	if err != nil {
		return err
	}
	defer out.Close()
	return d.copyWithProgress(out, resp.Body)
}

func (d *HTTPDownloader) mergeParts(filename string) error {
	out, err := d.createOutputFile(filename)
	if err != nil {
		return err
	}
	defer out.Close()

	for i := 0; i < len(d.partFiles); i++ {
		if d.partFiles[i] == "" {
			continue
		}
		tempFile := filepath.Join(d.config.TempDir, d.partFiles[i])
		part, err := os.Open(tempFile)
		if err != nil {
			return err
		}

		_, err = io.Copy(out, part)
		part.Close()
		if err != nil {
			return err
		}
	}
	//必须要加不然造成文件阻塞
	out.Close()

	// 在合并完成后验证并修复文件扩展名
	if err := d.VerifyAndFixFileExtension(filename); err != nil {
		fmt.Println("failed to verify and fix file extension:", err)
		return fmt.Errorf("failed to verify and fix file extension: %w", err)
	}
	return d.cleanupParts()
}

func (d *HTTPDownloader) VerifyAndFixFileExtension(filename string) error {
	// 获取文件真实类型
	contentType, err := d.VerifyFileType(filename)
	if err != nil {
		return err
	}

	// 获取当前文件扩展名
	currentExt := path.Ext(filename)
	baseName := strings.TrimSuffix(filename, currentExt)

	// 根据真实类型获取正确的扩展名
	correctExt := getCorrectExtension(contentType)
	if correctExt == "" {
		return nil // 无法确定正确扩展名，保持原样
	}

	// 如果扩展名不匹配，则重命名文件
	if !strings.EqualFold(currentExt, correctExt) {
		// 确保文件路径正确
		filename = filepath.Join(d.config.OutputDir, filename)
		newFilename := filepath.Join(d.config.OutputDir, baseName+correctExt)
		// 增加重试机制
		if renameErr := os.Rename(filename, newFilename); renameErr != nil {
			return fmt.Errorf("failed to rename file %w", renameErr)
		}
	}
	return nil
}

func getCorrectExtension(contentType string) string {
	exts, err := mime.ExtensionsByType(contentType)
	if err != nil || len(exts) == 0 {
		return ""
	}
	return exts[0] // 返回第一个匹配的扩展名
}

func (d *HTTPDownloader) cleanupParts() error {
	for _, file := range d.partFiles {
		filenamePath := filepath.Join(d.config.TempDir, file)
		if err := os.Remove(filenamePath); err != nil && !os.IsNotExist(err) {
			return err
		}
	}
	return nil
}

func (d *HTTPDownloader) getFilename(resp *http.Response) string {
	var filename string

	// Try Content-Disposition header first
	if cd := resp.Header.Get("Content-Disposition"); cd != "" {
		if _, params, err := mime.ParseMediaType(cd); err == nil {
			if fn := params["filename"]; fn != "" {
				filename = fn
			}
		}
	}

	// Try URL path if no filename found
	if filename == "" {
		if u, err := url.Parse(d.url); err == nil {
			if fn := path.Base(u.Path); fn != "" && fn != "." && fn != "/" {
				filename = fn
			}
		}
	}

	// Use default if still no filename
	if filename == "" {
		// Generate unique filename using timestamp to avoid conflicts
		timestamp := time.Now().UnixNano()
		filename = fmt.Sprintf("download_%d", timestamp)
	}

	// Get extension from Content-Type if filename has no extension
	ext := path.Ext(filename)
	// Check if extension matches Content-Type
	contentType := resp.Header.Get("Content-Type")
	if exts, err := mime.ExtensionsByType(contentType); err == nil && len(exts) > 0 {
		validExt := false
		for _, e := range exts {
			if strings.EqualFold(ext, e) {
				validExt = true
				break
			}
		}
		if !validExt {
			// Add correct extension if current one is invalid
			filename += exts[0]
		}
	}

	return filename
}

// 验证文件真实类型
func (d *HTTPDownloader) VerifyFileType(filePath string) (string, error) {
	filename := filepath.Join(d.config.OutputDir, filePath)
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()

	// Read the first 512 bytes to determine the file type
	buffer := make([]byte, 512)
	_, err = file.Read(buffer)
	if err != nil && err != io.EOF {
		return "", err
	}
	// Use the DetectContentType function to determine the MIME type
	contentType := http.DetectContentType(buffer)
	return contentType, nil
}

func (d *HTTPDownloader) Stop() error {
	close(d.stopChan)
	return nil
}

func (d *HTTPDownloader) Progress() float64 {
	d.mu.Lock()
	defer d.mu.Unlock()
	if d.totalSize == 0 {
		return 0
	}
	return float64(d.downloaded) / float64(d.totalSize) * 100
}

func (d *HTTPDownloader) Speed() int64 {
	// Implementation for speed calculation
	return 0 // TODO: Implement speed calculation
}
